﻿namespace clowncar
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using System.Net;
    using ICSharpCode.SharpZipLib.Zip;
    using clowncar.Utils;

    class Settings
    {
        internal string Url { get; set; }
        internal string LocalPath { get; set; }
        internal string TempFileName { get; set; }
        internal string TargetFileName { get; set; }
        internal bool Unzip { get; set; } 
        internal bool RunIt { get; set; }
        internal string Args { get; set; }

        internal bool Download()
        {
            using (var c = new WebClient())
            {
                c.Proxy = WebRequest.DefaultWebProxy;
                c.Proxy.Credentials = System.Net.CredentialCache.DefaultCredentials;

                try
                {
                    c.DownloadFile(this.Url, this.TempFileName);
                }
                catch (WebException ex)
                {
                    IOUtils.ConsoleError("Error downloading file", ex);
                    return false;
                }
            }
            return true;
        }

        string[] fileNames = null;

        /// <summary>
        /// Get the downloaded file (or the files produced by the zip) and
        /// copy them to their final resting place.
        /// 
        /// Very likely this means we will overwrite the calling executable
        /// If it is in use, it will hopefully have been renamed out of the way by now.
        /// </summary>
        internal void CopyZipContents()
        {
            bool useTargetExecutable = false;
            if (fileNames == null)
            {
                //We didn't unzip any files...
                //so use the downloaded file itself.
                fileNames = new string[] { TempFileName };
                useTargetExecutable = true;
            }
            
            foreach (string fileName in fileNames)
            {
                //Copy the file into place
                //...fileName might be 'C:\temp\random\hello.exe', so targetFile becomes 'C:\Program Files\App\hello.exe'
                
                var targetFile = Path.Combine(this.LocalPath, Path.GetFileName(fileName));
                if (useTargetExecutable)
                {
                    targetFile = Path.Combine(this.LocalPath, Path.GetFileName(TargetFileName));
                }
            
                System.IO.File.Copy(fileName, targetFile, true);
            }
        }

        internal void Copy()
        {
            
            var fileName = TempFileName;
                //Copy the file into place
                //...fileName might be 'C:\temp\random\1234.tmp', so targetFile becomes 'C:\Program Files\App\kv.exe'

                var    targetFile = Path.Combine(this.LocalPath, Path.GetFileName(TargetFileName));

                System.IO.File.Copy(fileName, targetFile, true);
            
        }

        /// <summary>
        /// Execute the new file. Only if we should.
        /// </summary>
        /// <returns></returns>
        internal bool Execute()
        {
            if (this.RunIt)
            {
                //System.Threading.Thread.Sleep(5000);
                try
                {
                    var proc = new Process();
                    proc.EnableRaisingEvents = false;
                    
                    proc.StartInfo.FileName = this.TargetFileName;
                    proc.StartInfo.Arguments = this.Args;
                    proc.StartInfo.UseShellExecute = false;
                    proc.StartInfo.RedirectStandardOutput = false;
                    
                    proc.Start();

                }
                catch (Exception ex)
                {
                    IOUtils.ConsoleError("Error launching file", ex);
                    return false;
                }
            }
            return true;
        }

        internal bool Wait()
        {
            var processes = Process.GetProcessesByName(Path.GetFileNameWithoutExtension(TargetFileName));

            if (processes.Length > 0)
            {
                foreach (var p in processes)
                {
                    if (p.MainModule.FileName == TargetFileName)
                    {
                        //TODO: Clean up these files. When?
                        System.IO.File.Move(TargetFileName, TargetFileName + "." + DateTime.Now.ToString("yyyyMMddHHmmss"));
                        break; //no point looking further.
                    }
                }
            }

            return true;
        }

        internal void UnzipTarget()
        {
            fileNames = UncompressZip(this.TempFileName);
        }

        private string[] UncompressZip(string sFile)
        {
            List<string> outputFileNames = new List<string>();
            string outputLocation = System.IO.Path.GetTempPath();
            using (ZipInputStream zipin = new ZipInputStream(File.OpenRead(sFile)))
            {
                ZipEntry entry;
                while ((entry = zipin.GetNextEntry()) != null)
                {
                    string outputFile = System.IO.Path.Combine(outputLocation, entry.Name);
                    //TODO: Uncompress to the right location.

                    //if we haven't yet worked out the name of the main executable, try and derive it now...
                    if (string.IsNullOrEmpty(this.TargetFileName) && Path.GetExtension(outputFile).ToLower() == ".exe")
                    {
                        this.TargetFileName = Path.Combine(this.LocalPath, Path.GetFileName(outputFile));
                    }

                    using (FileStream streamWriter = File.Create(outputFile))
                    {
                        outputFileNames.Add(outputFile);
                        long size = entry.Size;
                        byte[] data = new byte[size];
                        while (true)
                        {
                            size = zipin.Read(data, 0, data.Length);
                            if (size > 0)
                            {
                                streamWriter.Write(data, 0, (int)size);
                            }
                            else
                            {
                                break;
                            }
                        }

                        streamWriter.Close();
                    }
                }
            }

            return outputFileNames.ToArray();
        }
    }
}